/**
 * Copyright 2011 classic-commerce
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package ca.ioniq.server.service.impl;

import java.io.IOException;

import javax.inject.Inject;

import ca.ioniq.server.dao.UserDAO;
import ca.ioniq.server.service.AuthenticationService;
import ca.ioniq.server.service.exception.AuthenticationException;
import ca.ioniq.server.service.security.EncodingService;
import ca.ioniq.shared.entity.Credential;
import ca.ioniq.shared.entity.Role;
import ca.ioniq.shared.entity.User;

import com.google.inject.persist.Transactional;

public class AuthenticationServiceImpl implements AuthenticationService {

    private UserDAO userDao;
    private EncodingService encoder = new EncodingService();

    @Inject
    public AuthenticationServiceImpl(UserDAO userDao) {
        this.userDao = userDao;
    }

    /**
     * Create a new user account
     * pre-condition: check if username already exist
     *
     * @param user
     * @return
     */
    @Transactional
    public User createAccount(User user) throws AuthenticationException {
        User result = null;
        User userByName = userDao.findByName(user.getUserName());

        if (userByName != null)  {
            throw new AuthenticationException("name already exists!");
        } else {
        	            
        	Credential credential = encoder.getCredentialFromClearPassword(user.getCredential().getPassword());
            user.setCredential(credential);
        	userDao.persist(user);
            
            result = userDao.findByName(user.getUserName());
        }

        return result;
    }

	/**
     * This method allows to delete an existing user account.
     *  1. verify principal role
     *     1.1 verify that subject and principal belong to the same company
     *  2. delete account
     *
     * @param subject   the user whose account the caller wishes to delete
     * @param principal the user executing the call. This user must have
     *                  an admin role.
     * @return
     * @throws AuthenticationException
     *
     *  Side note: send email to advise user that his/her account has been deleted?
     */
    public boolean deleteAccount(User subject, User principal) throws AuthenticationException {
        if (principal.getRole() != null && principal.getRole() == Role.SUPER_ADMIN.getId()) {
            if (subject.getId() == null) { 
            	throw new AuthenticationException("This subject user doesn't have a associated id. Can't delete it.");
            }
            
            subject = userDao.findById(subject.getId());
            if (subject == null) { 
            	throw new AuthenticationException("This subject user is not in database. Can't delete it.");
            }
            
            userDao.delete(subject);
            
            return true;
        } 
        else { 
        	throw new AuthenticationException("This principal in not a administrator user or don't have an associated role");
        }
    }
    
    @Transactional
    public boolean updateAccount(User subject, User principal) throws AuthenticationException {
        // user can update his/her own account
        if(principal.getId() == subject.getId()) {
            userDao.update(subject);
            
            return true;
        }

        // administrator can delete a user account
        if (principal.getRole() != null && principal.getRole() == Role.SUPER_ADMIN.getId()) {
            if (subject.getId() == null) { 
            	throw new AuthenticationException("This subject user doesn't have a associated id. Can't update it.");
            }
            
            subject = userDao.findById(subject.getId());
            if (subject == null) { 
            	throw new AuthenticationException("This subject user is not in database. Can't update it.");
            }
            
            userDao.update(subject);
            
            return true;
            
        } else {
            throw new AuthenticationException("This principal in not a administrator user or don't have an associated role");
        }
    }

    /**
     * @param subject the {@link User} to be authenticated
     * @return true if authentication succeeded, false otherwise
     * @throws ca.ioniq.server.service.exception.AuthenticationException
     */
    public boolean authenticate(User subject) throws AuthenticationException {
		try {
	    	
			User repositoryUser = userDao.findByName(subject.getUserName());
	    	if(repositoryUser != null) { 
	    		return encoder.verifyPassword(subject.getCredential().getPassword(), repositoryUser.getCredential());
	    	}
			else { 
				return false;
			}
	    	
		} catch (IOException e) {
            throw new AuthenticationException("invalid user passord.", e);

		}
    }
}
